package priv.pfz.basedao.lab.performance;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static org.junit.Assert.assertEquals;

/**
 * @author yyjxp
 * 2024/8/4 15:34
 */
public class LambdaMethod implements ReflectTest {
    private final List<Function> functions = Lists.newArrayList();

    public LambdaMethod() {
        try {
            Map<String, Method> getterMap = Maps.newHashMap();
            BeanInfo beanInfo = Introspector.getBeanInfo(TestBean.class);
            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
                getterMap.put(pd.getName(), pd.getReadMethod());
            }
            for (Field field : TestBean.class.getDeclaredFields()) {
                Method method = getterMap.get(field.getName());
                functions.add(getFunction(method));
            }
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
        assertEquals(10, functions.size());
    }

    private Function getFunction(Method getter) {
        try {
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle methodHandle = lookup.unreflect(getter);
            return (Function) LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    MethodType.methodType(Function.class),
                    MethodType.methodType(Object.class, Object.class),
                    methodHandle,
                    methodHandle.type()).getTarget().invoke();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String name() {
        return "构造getter的lambda并调用";
    }

    @Override
    public void run(TestBean bean) {
        for (Function function : functions) {
            function.apply(bean);
        }
    }
}
